package com.ibm.spatiotemp.utils;

import java.util.Collection;
import java.util.Iterator;

import com.ibm.spatiotemp.server.datamodel.gpsdata.TSPoint;


/**
 * Implement a FIrst-In-First-Out queue.
 */
public class Queue {
	private static int MaxQueueLength = 1000000;
	/** First entry. */
	private Entry _head = null;
	/** Last entry. */
	private Entry _tail = null;
	/** Number of entries. */
	private int _count = 0;
	private boolean emptyWFlag = false;
	private boolean fullWFlag = false;
	private int maxLength = 1000000;
	
	public Queue() {
		super();
		this.maxLength = MaxQueueLength;
	}
	
	public Queue(int maxLength) {
		super();
		this.maxLength = maxLength;
	}
	
	/** 
	 * Add an element to the queue. 
	 * When queue is full, wait * 
	 */
	public synchronized void put(Object element) throws InterruptedException{
		while (_count >= maxLength) {
			// the queue is full
			fullWFlag = true;
			wait();
		}
		Entry entry = new Entry();
		entry._element = element;
		entry._next = null;
		if (_tail == null) 
			_head = entry;
		else
			_tail._next = entry;
		_tail = entry;
		_count++;
		if (emptyWFlag) {
			emptyWFlag = false;
			notifyAll();
		}
	}
	
	
	/** 
	 * Add a entry (link) to the queue. 
	 * When queue is full, wait
	 */
	public synchronized void put(Entry dataEntry) throws InterruptedException{
		int size = dataEntry.getSize();
		while ((_count + size) >= maxLength) {
			// the queue is full
			fullWFlag = true;
			wait();
		}
		// add entry to the tail
		if (_tail == null) {
			_head = dataEntry;
		} else {
			_tail._next = dataEntry;
		}
		// reset _tail to the last one
		Entry prev = null;
		while (dataEntry != null) {
			prev = dataEntry;
			dataEntry = dataEntry.getNext();
		}
		_tail = prev;	
		_count = _count + size;
		if (emptyWFlag) {
			emptyWFlag = false;
			notifyAll();
		}
	}
	
	
	/** 
	 * Add a list to the queue. 
	 * When queue is full, wait
	 */
	public synchronized void putPoints(Collection<TSPoint> elements) throws InterruptedException{
		int size = elements.size();
		while ((_count + size) >= maxLength) {
			// the queue is full
			fullWFlag = true;
			wait();
		}
		Iterator<TSPoint> iter = elements.iterator();
		while (iter.hasNext()) {
			Entry entry = new Entry();
			entry._element = iter.next();
			entry._next = null;
			if (_tail == null) {
				_head = entry;
			} else {
				_tail._next = entry;
			}
			_tail = entry;
		}
		_count = _count + size;
		if (emptyWFlag) {
			emptyWFlag = false;
			notifyAll();
		}
	}
	
	/** Tests whether the queue is empty. */
	public boolean isEmpty() { return _count == 0; }
	/** Returns the number of elements on the queue. */
	public int size() { return _count; }
	
	/** 
	 * Add an element to the queue. 
	 * When queue is full, return false
	 */
	public synchronized boolean add(Object element) {
		if (_count >= maxLength) {
			// the queue is full
			return false;
		}
		Entry entry = new Entry();
		entry._element = element;
		entry._next = null;
		if (_tail == null) 
			_head = entry;
		else
			_tail._next = entry;
		_tail = entry;
		_count++;
		return true;
	}
	/** Remove an element from the queue. 
	 * When queue is empty, return null
	 */
	public synchronized Object remove() {
		if (_head == null) 
			return null;
		else {
			Entry entry = _head;
			_head = entry._next;
			if (_head == null)
				_tail = null;
			_count--;
			entry._next = null;
			return entry._element;
		}
	}
	
	/**
	 * Take an element from the queue.
	 * When the queue is empty, wait
	 * @return the element
	 */
	public synchronized Object take() throws InterruptedException {
		while (_head == null) {
			// the queue is empty
			emptyWFlag = true;
			wait();			
		}		
		Entry entry = _head;
		_head = entry._next;
		if (_head == null)
			_tail = null;
		_count--;
		entry._next = null;
		if (fullWFlag) {
			fullWFlag = false;
			notifyAll();
		}
		return entry._element;
	}
	
	/**
	 * Take all elements from the queue.
	 * When the queue is empty, wait
	 * @return the element
	 */
	public synchronized Entry takeAll() throws InterruptedException {
		while (_head == null) {
			// the queue is empty
			emptyWFlag = true;
			wait();			
		}		
		Entry entry = _head;
		_head = null;
		_tail = null;
		_count = 0;
		if (fullWFlag) {
			fullWFlag = false;
			notifyAll();
		}
		return entry;
	}
	
	public synchronized void clear() {
		_head = null;
		_tail = null;
		_count = 0;
	}
	
	public static synchronized void setMaxLength(int length) {
		MaxQueueLength = length;
	}

	public static int getMaxQueueLength() {
		return MaxQueueLength;
	}	
	
}